package reference;

import apply.data.Person;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 方法引用
 * 1. eg
 * Integer :: compare
 * 2.方法引用的解释
 * > 方法引用，可以看作是基于lambda表达式的进一步刻画。
 * > 当需要提供一个函数式接口的实例时，可以使用lambda表达式提供此实例。
 * ---当满足一定的条件的情况下，还可以使用方法引用或构造器引用替换lambda表达式。
 * <p>
 * 3.方法引用的本质：方法引用作为了函数式接口的实例。
 * <p>
 * 4.格式
 * 类(或对象) :: 方法名
 * <p>
 * 5.具体使用情况说明：
 * 情况一：  对象 :: 实例方法
 * --------要求：函数式接口中的抽象方法 A 与其内部实现时调用的对象的某个方法 B 的形参列表和返回值类型都相同(或一致)。
 * -------------此时可以考虑使用方法 B 实现对方法 A 的替换、覆盖。此替换或覆盖即为方法引用。
 * --------注：此时 B 是非静态方法，需要对象调用。
 * 情况二：  类 :: 静态方法
 * --------注：此时 B 是静态方法，需要类调用。
 * 情况三：  类 :: 实例方法
 * ---------要求：函数式接口中的抽象方法 A 与其内部实现时调用的对象的某个方法 B 的返回值类型相同。
 * --------------同时，抽象方法A中有n个参数，方法B中有n-1个参数，且抽象方法A的第一个参数作为方法B的调用者，
 * --------------且抽象方法A的后n-1个参数与方法B的n-1个参数的类型相同(或一致)。
 * --------------则可以考虑使用方法B实现对方法A的替换、覆盖。此替换或覆盖即为方法引用。
 * ----------注：此方法B是非静态方法，需要对象调用。但是形式上，写成对象A所属的类。
 */
public class MethodRefTest {
    /**
     * 情况一：  对象 :: 实例方法
     */
    @Test
    public void test1() {
        // 1.
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.print(s);
            }
        };
        consumer.accept("Hello");

        // 2.lambda表达式
        Consumer<String> consumer1 = s -> System.out.print(s);
        consumer1.accept(" World");

        // 3.方法引用
        Consumer<String> consumer2 = System.out::print;
        consumer1.accept("!");
    }

    @Test
    public void test2() throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Class<Person> personClass = Person.class;
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        Person person = declaredConstructor.newInstance("Tom", 13);
        Field age = personClass.getDeclaredField("age");
        age.setAccessible(true);

        Field name = personClass.getDeclaredField("name");
        name.setAccessible(true);
        System.out.println(name.get(person).getClass()); // class java.lang.String
        Supplier<Integer> supplier = () -> person.age;
        System.out.println(supplier.get());

        Supplier<String> stringSupplier = () -> {
            try {
                return name.get(person).toString();
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        };

        System.out.println(stringSupplier.get());

        Supplier<Integer> stringSupplier2 = age::getModifiers;
        System.out.println(Modifier.toString(stringSupplier2.get()));

    }

    /**
     * 情况二：  类 :: 静态方法
     */
    @Test
    public void test3() {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        System.out.println(comparator.compare(12, 23));

        Comparator<Integer> comparator1 = Integer::compare;
        System.out.println(comparator1.compare(23, 12));

        Function<Double, Long> function = new Function<Double, Long>() {
            @Override
            public Long apply(Double aDouble) {
                return Math.round(aDouble);
            }
        };

        Function<Double, Long> function1 = Math::round;
        System.out.println(function1.apply(1.98));

    }

    /**
     * 情况三：  类 :: 实例方法
     */
    @Test
    public void test4() {
        Comparator<String> comparator = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        };

        Comparator<String> comparator1 = (o1, o2) -> o1.compareTo(o2);

        Comparator<String> comparator2 = String::compareTo;
        System.out.println(comparator2.compare("a", "A")); // 32

        BiPredicate<String, String> biPredicate = new BiPredicate<String, String>() {
            @Override
            public boolean test(String s, String s2) {
                return s.equals(s2);
            }
        };

        System.out.println(biPredicate.test("AA", "AA"));

        BiPredicate<String, String> biPredicate1 = String::equals;
        System.out.println(biPredicate1.test("AA", "BB"));
    }

    @Test
    public void testEmployee() {
        Employee employee = new Employee();
        employee.setName("Steff");
        Function<Employee, String> function2 = new Function<Employee, String>() {
            @Override
            public String apply(Employee employee) {
                return employee.getName();
            }
        };
        Function<Employee, String> function1 = emp -> emp.getName();

        Function<Employee, String> function = Employee::getName;
        System.out.println(function.apply(employee));
    }
}

